package org.example.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.example.entity.control.BmsControlDTO;
import org.example.mqtt.MqttService;
import org.example.util.DeviceUtil;
import org.example.util.Result;
import org.example.util.constantUtil.ConstantUtil;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Slf4j
@Service
@RequiredArgsConstructor
public class BmsControlService {
    private final MqttService mqttService;

    public Result bmsControl (BmsControlDTO bmsControlDTO) {
        String imei = bmsControlDTO.getImei();
        int functionCode = bmsControlDTO.getFunctionCode();
        int[] controlCode = bmsControlDTO.getControlCode();

        // 获取客户customerTitle
        String customerTitle= DeviceUtil.ImeiCustomerTitle.get(imei);
        log.info("Device controlled is {}",customerTitle);
        if(customerTitle==null||customerTitle.equals("")){
            return Result.fail("Cannot get CustomerTitle 获取客户信息失败");
        }

        // 拼接C2帧
        String codeC2 = buildC2Frame(functionCode, controlCode);

        // 构造完整消息，拼接customerTitle和C2帧
        String messageC2=TimeseriesService.stringToHexAscii(customerTitle)+codeC2;
        log.info("messageC2: "+messageC2);

        // 注册电池包到CompletableFuture
        CompletableFuture<String> future = new CompletableFuture<>();
        mqttService.registerCallback(imei, functionCode, future);

        // 发送消息并等待结果，
        String response;
        try {
            String topicPub= ConstantUtil.TOPIC_PREFIX + imei + ConstantUtil.PUB_TOPIC_SUFFIX_CONTROL;
            mqttService.publishMessage(topicPub, messageC2);
            response = future.get(ConstantUtil.CONTROL_TTL_SECOND, TimeUnit.SECONDS);  // 超时时间内
        } catch (MqttException e) {
            log.error("控制指令发送失败, imei:{}, funcCode:{}, controlCode:{}", imei, functionCode, controlCode, e);
            return Result.fail("send mqtt message failed!");
        }
        catch (TimeoutException e) {
            log.error("控制指令接收失败, imei:{}, funcCode:{}, controlCode:{}", imei, functionCode, controlCode, e);
            return Result.fail("get response time out");
        } catch (Exception e) {
            log.error("未知错误, imei:{}, funcCode:{}, controlCode:{}", imei, functionCode, controlCode, e);
            return Result.fail("get response failed");
        }
        finally {
            // 不管收没收到这条最后都要删掉
            mqttService.unregisterCallback(imei, functionCode);
        }

        return getSetResult2_5(response);
    }



    // 拼接C2帧
    private static String buildC2Frame(int functionCode, int[] controlCode) {
        StringBuilder sb = new StringBuilder();
        // 8个字节的16进制
        // 拼接功能码
        sb.append(decimalToHex(functionCode));
        // 拼接控制码，共C2.len - 1个控制字节
        for (int i = 0; i < ConstantUtil.FRAME_C2_BYTES - 1; i++ ) {
            // 存在控制数据
            if (i < controlCode.length) {
                sb.append(decimalToHex(controlCode[i]));
            } else {  // 剩余补0
                sb.append("00");
            }
        }
        return sb.toString().toUpperCase();
    }

    // 整数转换为1个字节的十六进制
    private static String decimalToHex(int decimal) {
        String hex = Integer.toHexString(decimal);
        return hex.length() == 1 ? "0" + hex : hex;
    }

    // 协议2.5直接通过C1的第二个字段判断是否设置成功
    private Result getSetResult2_5(String response){
        log.info("Protocol v2.5");
        if(response==null){
            //发送到mqtt启动轮询
            return Result.fail("response is null");
        }
        if(response.contains("message"))
        {
            return Result.fail("MQTT消息推送失败");
        }
        // 获取控制结果字段
        if(response.length()>4){
            // xx 01 xx xx xx xx xx xx
            response=response.substring(2,4);
        }
        log.info("Control Result:{}",response);
        if(!"01".equals(response)){
            return Result.fail("send success,but set failed");
        }
        return Result.ok("true");
    }
}

